﻿namespace SimpleAssemblyBrowser

module Helpers =
    type 'a Tree = Node of 'a*('a Tree [])


module Resolution = 
    //returns a list of the referenced assemblies
    let getReferencedAssemblies(name: string): string[] = 
        failwith "na"

    type Result = Success of Helpers.Tree<string> 
                    | Error of string
        
    let getReferenceAssembliesRecursively(name: string): Result = 
        failwith "na"

module Visitors = 
    open Mono.Cecil

    type IMethodVisitor<'I,'O> = 
        abstract member Visit: MethodDefinition -> 'I -> 'O
            

    type ITypeVisitor<'I,'O> = 
        abstract member Visit: TypeDefinition -> 'I -> 'O

    type IModuleVisitor<'I,'O> = 
        abstract member Visit: ModuleDefinition -> 'I -> 'O
            
    type IAssemblyVisitor<'I,'O> = 
        abstract member Visit: AssemblyDefinition -> 'I -> 'O

    type IVisitor<'A,'O,'T,'M,'R> = 
        abstract member GetMethodVisitor: unit -> IMethodVisitor<'M,'R>
        abstract member GetTypeVisitor: unit -> ITypeVisitor<'T,'M>
        abstract member GetModuleVisitor: unit -> IModuleVisitor<'O,'T>
        abstract member GetAssemblyVisitor: unit -> IAssemblyVisitor<'A,'O>
                 
//    let getAllTypes (moduleDef: ModuleDefinition): TypeDefinition [] =
//        let rec getNestedTypes (t: TypeDefinition) = 
//            seq{
//                yield! t.NestedTypes
//                for n in t.NestedTypes do
//                    yield! (getNestedTypes n)
//            }
//        [|
//            yield! moduleDef.Types
//            for t in moduleDef.Types do
//                yield! (getNestedTypes t)
//        |]
//             
//    let visitAllTypes (moduleDef: ModuleDefinition) 
//                      (typeVisitor: ITypeVisitor<_,_>) 
//                      (methodVisitor: IMethodVisitor<_,_>)
//                      (parent: 'X) =
//
//        let rec visitTypes (typeDefs: seq<TypeDefinition>) (t: 'X) = 
//            for typeDef in typeDefs do 
//                let m = typeVisitor.Visit typeDef t
//                for methodDef in typeDef.Methods do
//                    let _ = methodVisitor.Visit methodDef m
//                    ()
//                visitTypes typeDef.NestedTypes t
//        visitTypes (moduleDef.Types)
                   
    let visitRecursively<'A,'O,'T,'R>
                        (visitor: IVisitor<'A,'O,'T,'T,'R>) 
                        (input: 'A)
                        (assemblyDef: AssemblyDefinition) : unit = 
        let assemblyVisitor = visitor.GetAssemblyVisitor()
        let moduleVisitor = visitor.GetModuleVisitor()
        let typeVisitor = visitor.GetTypeVisitor()
        let methodVisitor = visitor.GetMethodVisitor()
        let o = assemblyVisitor.Visit assemblyDef input
        let rec visitTypes (typeDefs: seq<TypeDefinition>) (t) = 
            
            for typeDef in typeDefs do 
                
                let m = typeVisitor.Visit typeDef t
                for methodDef in typeDef.Methods do
                    let _ = methodVisitor.Visit methodDef m
                    ()
                visitTypes typeDef.NestedTypes m
        for moduleDef in assemblyDef.Modules do
            let t = moduleVisitor.Visit moduleDef o
            
            visitTypes (moduleDef.Types) t
//            for typeDef in getAllTypes moduleDef do 
//                let m = typeVisitor.Visit typeDef t
//                for methodDef in typeDef.Methods do
//                    let _ = methodVisitor.Visit methodDef m
//                    ()

module CodeUtils = 
    open Mono.Cecil

    let dumpCode (methodDef: MethodDefinition) = 
        methodDef.Body.Instructions
        |> Seq.map(fun instr -> instr.ToString())
        |> Seq.iter(printfn "%s")
    
    //D:\Cecil\jbevain-cecil-06279d0\Mono.Cecil.Cil\OpCodes.cs      
//    	Branch,
//		Break, -- ignore
//		Call, -- ignore
//		Cond_Branch, -- treat as branch
//		Meta,
//		Next,
//		Phi,
//		Return,
//		Throw,

    //to do: (is duplicated)
    let toDict(input:seq<'k*'v>) = 
        let d = System.Collections.Generic.Dictionary<_,_>()
        for (k,v) in input do
            d.Add(k,v)
        d

    let toGraph(instrs: Cil.Instruction[]): StronglyConnectedComponents.Graph<Cil.Instruction> = 
        let nodes = instrs |> Seq.map(fun instr -> instr, StronglyConnectedComponents.newNode(instr))
                    |> toDict

        let getNextInstrunctions(i: int) (instr: Cil.Instruction) = 
            let flowControl = instr.OpCode.FlowControl
            match flowControl with
             | Cil.FlowControl.Meta -> [|instrs.[i + 1]|]
             | Cil.FlowControl.Call -> [|instrs.[i + 1]|]
             | Cil.FlowControl.Next -> [|instrs.[i + 1]|] //to do: handle incorrect il
             | Cil.FlowControl.Branch -> [|instr.Operand :?> Cil.Instruction |]
             | Cil.FlowControl.Cond_Branch -> [|instrs.[i + 1]; instr.Operand :?> Cil.Instruction |]
             | Cil.FlowControl.Throw -> Array.empty
             | Cil.FlowControl.Return -> Array.empty
             | _ -> failwith (sprintf "Oops %A is unhandled" flowControl)
        instrs |> Seq.iteri(fun i instr -> let links = getNextInstrunctions i instr
                                           let node = nodes.[instr]
                                           for link in links do
                                              node.Links.Add(nodes.[link]))
        nodes.Values |> Array.ofSeq |> StronglyConnectedComponents.Graph
//          
//                        
    let basicBlocks(instrs: Cil.Instruction[]): Cil.Instruction[][] = 
        let graph = toGraph instrs
        StronglyConnectedComponents.stronglyConnectedComponents graph |> fst
        |> Array.map(fun c -> 
                        Array.map(fun (node: StronglyConnectedComponents.Node<Cil.Instruction>) -> node.Value) c
                        |> Array.sortBy(fun instr -> instr.Offset)
                        )
    
    type InstructionVariable = {
        mutable argType: string; //to do: change
        mutable argName: string;
    }

    type InstructionStack = {
        mutable ready: bool;
        mutable stack: list<InstructionVariable>
    }    
    //instruction stack

    module Unif = 
        type 
            [<ReferenceEquality>]
            ValueType<'a> = Single of 'a | Pair of Cell<'a>*Cell<'a> | Nil
        and 
            [<ReferenceEquality>]
            Value<'a> = Free | Bound of ValueType<'a> // 'a | Pair of Cell<'a>*Cell<'a> | Nil
        and
            [<ReferenceEquality>]
            CellType<'a> = Final of Value<'a> | Next of Cell<'a>
        and 
            [<ReferenceEquality>]
            Cell<'a> = Cell of CellType<'a> ref // | Cons of (Cell<'a>*Cell<'a>)
        //type Cell<'a> = Cell of Cell<'a> ref| Free | Bound of 'a
//        type MutCell<'a> = 
//            let mutable parent =  Free
//
//        let atom(x:'a) = Bound x
//        let free<'a> () = Cell(ref Free)
//

        type DisplayValueType<'a> = | DSingle of 'a 
                                    | DPair of DisplayValueType<'a>*DisplayValueType<'a>
                                    | DNil
                                    | DFree
                                                                     
        let rec walkup (Cell rx: Cell<'a>) = 
            match !rx with
             | Final v -> (v,rx)
             | Next c -> walkup c

        let setNext (r: CellType<'a> ref) (v: Cell<'a>) = 
            r := Next v

        let getCellType(Cell x) = !x
        let baseCaseValue(x: Value<'a>) = Cell(ref (Final(x)))
            
        let free () = baseCaseValue(Free) // Cell(ref (Final(Free)))
        let nil () = baseCaseValue(Bound(Nil)) // Cell(ref (Final(Free)))
        let atom (x: 'a) = baseCaseValue(Bound(Single x)) // Cell(ref (Final(Bound (Single x))))
        let cons (h: Cell<'a>, t: Cell<'a>) = baseCaseValue(Bound(Pair (h,t)))



            //Cell(ref (Final(Bound (Pair (h,t)))))
            
        let rec occurs (x: Cell<'a>) (ct: Cell<'a>) =
            if (x = ct) then
                true
            else
                match getCellType ct with
                 | Final v-> 
                    match v with
                     | Bound(Pair(c1,c2)) -> (occurs x ( c1))
                                              || (occurs x ( c2))
                     | _ -> false
                 | Next c -> if c = x then true
                             else 
                                occurs x ( c)
        let rec bind (x: Cell<'a>) (y: Cell<'a>) = 
            let x_, px = walkup x
            let y_, py = walkup y
            match x_, y_ with
             | Free, Free -> let common = free()
                             setNext px common
                             setNext py common
                             true
             | Free, Bound _ -> if occurs x y then
                                    false
                                else
                                    setNext px (Cell py) //(getCellType y_)//problems if bound is a tree that contains x
                                    true
             | Bound _, Free -> 
                                if occurs y x then
                                    false
                                else
                                    setNext py (Cell px) //(getCellType x_)
                                    true
             | Bound vx, Bound vy ->
                match vx, vy with
                 | Nil, Nil -> true
                 | Single a, Single b -> a = b
                 | Pair (a1,a2), Pair (b1,b2) -> 
                                               (bind a1 a2) && (bind b1 b2)
                 | _ -> false
                                                    
        let ht (x: Cell<'a>) = 
            let x_, px = walkup x
            match x_ with
             | Bound v ->
                            match v with
                             | Pair (h,t) -> (h,t)
                             | _ -> failwith "not a cons"
             | Free -> 
                let h,t = free(), free()
                let c = cons (h,t)
                bind x c
                h,t
        let hd (x: Cell<'a>) = ht x |> fst
        let tl (x: Cell<'a>) = ht x |> snd

        let ensureBound (x: bool) = 
            if not x then failwith "bind failed"
        let bindX x y = bind x y |> ensureBound
        let rec valueOf(x: Cell<'a>): DisplayValueType<'a> = 
            let x_, v = walkup x
            match x_ with
             | Free -> DFree
             | Bound vt ->
                match vt with
                 | Nil -> DNil
                 | Single v -> DSingle v
                 | Pair (a,b) -> DPair(valueOf a, valueOf b)
//
//                Single of 'a | Pair of Cell<'a>*Cell<'a> | Nil


        let test () = 
            let a = free()
            let b = free()
            bind a b |> ensureBound
            let c = atom(20)
            bind a c |> ensureBound
            valueOf b |> printfn "%A"

            let lst1 = cons(free(), free())
            let lst2 = free()
            bindX lst1 lst2
            let h = hd lst2
            bindX h (atom 3)
            let t =  (tl lst1) 
            //bindX t (atom 5)
            //bindX t lst1 // lst1 //occurs check, fails on valueOf due to cyclic structure
            lst1 |> valueOf  |> printfn "%A"
            //valueOf k |> printfn "%A"
            let empty = nil()
            let nonEmpty = cons(free(), free())
            bindX empty nonEmpty
            ()
        
        //let last (x: Cell<'a>) = 
            
        let test2 () = 
            let x = free()
            let y = atom(1)
            //stack = c(X;c(Y;(c(Z))
            //top = hd stack
            //push v s = cons(v, s)
            //empty = nil()
            //pop s = tl s
            // X = [1;2;Y]
            // Y = [3;Z]
            // X = [1;2;Y]
            // 
            ()          


//        type Cell<'a when 'a: equality>() = 
//            let mutable value = ref Unbound
//
//            member this.Value with get() = value
                                //and set(x:'a) = value <- Bound x
            
           
//            X //unbound cell
//            X = Y //point to the same cell, but cell is free
//            X = 5 //set value to cell
//            member this.Bind(newValue: 'a ref) =
//                match value with
//                 | Unbound x -> value <- Bound newValue
//                 | Bound x -> if (x <> newValue) then
//                                failwith "unification error"
//            
//        type Atom<'a> = Value<'a>
//        let atom v = let x = Var()
//                     x.Bind(ref v)
//                     x
//        type List<'a> = Nil | Cons of (Atom<'a>*List<'a>)
//        let bind (x:Var<'a>) (y:Var<'a>) = 
//            match x.Value, y.Value with
//             | Unbound, Unbound -> ()
//             | Unbound, Bound v -> x.Bind v
//             | Bound v, Unbound -> y.Bind v
////             | Bind v, Bind w -> if (!v <> !w) then
////                                    fail

//
//    module TestUnif = 
//        open Unif
//        let single = Var()

    type StackVar = StackVar of string*string
    type UnifCell = Unif.Cell<StackVar>
    let showStack(methodDef: MethodDefinition) (instrs: Cil.Instruction[]) = 
        let revGraph = System.Collections.Generic.Dictionary<Cil.Instruction, Cil.Instruction[]>()
        let instr2Index = System.Collections.Generic.Dictionary<Cil.Instruction, int>()
        let solved = Array.init (instrs.Length) (fun _ -> false)
        let stacks = Array.init (instrs.Length) 
                                (fun i -> (Unif.free(), Unif.free()))
        Unif.bindX (fst stacks.[0]) (Unif.nil())

        let ensureEqualStacks (stack0: UnifCell) (stack1: UnifCell) = 
            Unif.bindX stack0 stack1


        let rec handleRecursively (i: int)  = 
            let (instr: Cil.Instruction) = instrs.[i]
            let before = fst stacks.[i]
            let after =  snd stacks.[i]
            

            let ldc (arg: int) = 
                let newVar = StackVar(arg.ToString(),"")
                ensureEqualStacks (Unif.cons(Unif.atom(newVar),before))
                                    after            

            let stloc (x: int) = 
                handleRecursively (i - 1)
                let var = methodDef.Body.Variables.[x]
                let x,before0 = Unif.ht before
                //x should have been solved
                //x may not have been computed
                let newVar = StackVar(var.ToString(),"=")
                ensureEqualStacks before0
                                  after    

            let linksTo = revGraph.[instr]
            for link in linksTo do
                ensureEqualStacks (fst stacks.[i]) (snd stacks.[instr2Index.[link]])
            match instr.OpCode.Code with
             | Cil.Code.Nop -> 
                    ensureEqualStacks before after
             | Cil.Code.Ldarg_S ->
                    let argIdx = instr.Operand :?> int
                    let pDef = methodDef.Parameters.[argIdx]
                    let newVar = StackVar(pDef.Name,"")
                    ensureEqualStacks (Unif.cons(Unif.atom(newVar),before))
                                      after

             | Cil.Code.Pop ->
                    ensureEqualStacks (Unif.tl before) after
                    
             | Cil.Code.Ldc_I4_0 -> ldc 0
             | Cil.Code.Ldc_I4_1 -> ldc 1
             | Cil.Code.Stloc_0 -> stloc 0
             | Cil.Code.Stloc_1 -> stloc 1
             | Cil.Code.Stloc_2 -> stloc 2
             | _ ->()
            
        ()
//    let showStackOnInstructions (instrs: Cil.Instruction[]) = 
//        //let instrToPos
//        let (stacks: InstructionStack[]) = Array.init (instrs.Length) (fun _ -> {ready = false;stack = []})
//        let getPrevStack(i: int) = 
//            stacks.[i].stack
//        let genName i = sprintf "tmp_%d" i
//        instrs |> Seq.iteri(fun i instr ->
//            let s = stacks.[i]
//            if not s.ready then
//                s.ready <- true
//                let flowControl = instr.OpCode.FlowControl
//                match flowControl with
//                    | Cil.FlowControl.Meta -> s.stack <- getPrevStack i
//                    | Cil.FlowControl.Call -> 
//                               //to do: verify previous arguments
//                               let mr = instr.Operand :?> MethodReference
//                               let nm = mr.ReturnType.Name
//                               s.stack <- [{argType = nm; argName = genName i}]
//                    | Cil.FlowControl.Next -> 
//                        
//                    | Cil.FlowControl.Branch -> 
//                        
//                        let target = instr.Operand :?> Cil.Instruction
//                        ()
//                        //let targetS = stacks.[instrToPos[target]]
//                        //targetS.ready <- true
//                        //targetS.stack <- []
//
//                    | Cil.FlowControl.Cond_Branch ->
//                        let target instr.Operand :?> Cil.Instruction
//                        //exects some stuff based on type
//                        ()
//                    | Cil.FlowControl.Throw -> 
//                        s.stack <- []
//                    | Cil.FlowControl.Return ->
//                               //verify, if returns non void, must expect at least one thing on the stack 
//                               s.stack <- []
//                               
//            else
//                printfn "must verify"
//
//        )
//        


//            IL_0009: ldloc.0, IL_000a: ldloc.1, IL_000b: ceq, IL_000d: ldc.i4.0, IL_000e: ce
//q, IL_0010: stloc.3, IL_0011: ldloc.3, IL_0012: brtrue.s IL_0017, IL_0014: nop,
//IL_0015: br.s IL_0009,
// need to do a series of inlining and elimination
// [x,y]
// if x = y then [res = 1] else [res = 0]
// [res;0]
// if res = 0 then [res2= 1] else [res2 = 0] 
// z = res2
// if res2 then goto 17
// goto start

//start:
// if x = y then goto start
// else goto 17

        

    
          
       
//   		public static readonly OpCode Nop = new OpCode (
//			0xff << 0 | 0x00 << 8 | (byte) Code.Nop << 16 | (byte) FlowControl.Next << 24,
//          
//			(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);    

    //let visitLazily()... (fix with 

module UI = 
    open System.Windows.Forms
    open Visitors

    type MainForm() as this =
        inherit Form()
        let treeView = new TreeView()
        do this.Initialize()
               
        member this.Initialize() = 
                
            this.Controls.Add(treeView)
            treeView.Dock <- DockStyle.Fill

        member this.AssemblyTreeView with get() = treeView
            
            
        //initialize components
            
    let createMainForm(): System.Windows.Forms.Form = 
        failwith "na"   
              
   
             
module AssemblyInfo2UI = 
    open UI
    open Visitors
    open Mono.Cecil
    type TreeView = System.Windows.Forms.TreeView
    type TreeNode = System.Windows.Forms.TreeNode

    let populateForm () = 
        let genericVisit (value: string) (node: TreeNode): TreeNode = 
            let childNode = TreeNode(value)
            node.Nodes.Add(childNode)
            childNode                
        let methodVisitor = 
            { new IMethodVisitor<TreeNode,TreeNode> with
                override this.Visit (methodDef: MethodDefinition) (node: TreeNode) = 
                    genericVisit (methodDef.Name) node
            }

        let typeVisitor =
            {new ITypeVisitor<TreeNode, TreeNode> with
                override this.Visit (typeDef: TypeDefinition) (node: TreeNode) = 
                        genericVisit (typeDef.Namespace + " # " + typeDef.Name) node
            }
            
        let moduleVisitor = 
            {new IModuleVisitor<TreeNode, TreeNode> with
                override this.Visit(moduleDef: ModuleDefinition) (node: TreeNode) = 
                    genericVisit (moduleDef.Name) node
            }

        let assemblyVisitor = 
            {new IAssemblyVisitor<TreeView, TreeNode> with
                override this.Visit(assemblyDef: AssemblyDefinition) (tree: TreeView) = 
                    let childNode = TreeNode(assemblyDef.Name.Name)
                    tree.Nodes.Add(childNode)
                    childNode  
            }

        let visitor = 
            {new IVisitor<TreeView,TreeNode,TreeNode,TreeNode,TreeNode> with
                override this.GetMethodVisitor() = methodVisitor
                override this.GetTypeVisitor() = typeVisitor
                override this.GetModuleVisitor() = moduleVisitor
                override this.GetAssemblyVisitor() = assemblyVisitor
            }
        let mainForm = new MainForm()
        let assemblyName = @"D:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll"

        let assemblyName = @"D:\DotNetTracer\DotNetTracer\TestProject\bin\Debug\TestProject.exe"
        //let assemblyName = @" D:\DotNetTracer\DotNetTracer\SimpleAssemblyBrowserInFSharp\bin\Debug\SimpleAssemblyBrowserInFSharp.exe"
        let assemblyDef = AssemblyDefinition.ReadAssembly(assemblyName)
        visitRecursively visitor (mainForm.AssemblyTreeView) assemblyDef
        mainForm.ShowDialog()
//        type ITypeVisitor<'I,'O> = 
//            abstract member Visit: TypeDefinition -> 'I -> 'O
//
//        type IModuleVisitor<'I,'O> = 
//            abstract member Visit: ModuleDefinition -> 'I -> 'O
//            
//        type IAssemblyVisitor<'I,'O> = 
//            abstract member Visit: AssemblyDefinition -> 'I -> 'O


module CodeDecompil = 
    open Mono.Cecil
    open Mono.Cecil.Cil
    
    //type VarDef = VarDef of string
    

    type BuiltInType2 = Add | Sub 
    type BuiltInType1 = Box of TypeReference

    type FlagCmp = Eq | Gt | Lt

    //to do:
    // use var for lhs and expr. for rhs
    [<ReferenceEquality>]
    type 
        VarDef =   //IntConst of int 
                     //| StringConst of string
                     //| Null
                     | Const of string //to do: Const of Consttype
                     //...other //to do: may need to change the type of the variable
                     | LocalVar of VariableDefinition
                     | MethodArg of ParameterDefinition
                     | Field of FieldDefinition
                     | FunPtr of MethodReference 
                     | TmpVar of Cil.Instruction*(option<TypeReference> ref)
                     | ExprOfCall of FuncCall
//                     | ExprOfCond of Cond
//    and
//        Expr =
//            | GetVar of VarDef
//            | ExprOfCall of FuncCall
//            | ExprOfCond of Cond
    and
        VarDefRef = VarDefRef of ref<VarDef>
    and
        FuncCall = FuncCall of MethodReference*VarDefRef[]
                    | BuiltInCall1 of BuiltInType1*VarDefRef
                    | BuiltInCall2 of BuiltInType2*VarDefRef*VarDefRef
                    | Flag of FlagCmp*VarDefRef*VarDefRef
                     //| Field of string
    and
        Cond =   CondTrue of VarDefRef
                | CondFalse of VarDefRef
                | CondEq of VarDefRef*VarDefRef
                | CondNeq of VarDefRef*VarDefRef
                | CondLt of VarDefRef*VarDefRef
                | CondGt of VarDefRef*VarDefRef
                | CondGtEq of VarDefRef*VarDefRef
                | CondLtEq of VarDefRef*VarDefRef

    let getVarDefRefFromCond (c: Cond) = 
        match c with 
         | CondTrue r -> [|r|] 
         | CondFalse r -> [|r|] 
         | CondEq (r1,r2) -> [|r1;r2|] 
         | CondNeq (r1,r2) -> [|r1;r2|] 
         | CondLt (r1,r2) -> [|r1;r2|] 
         | CondGt (r1,r2) -> [|r1;r2|] 
         | CondGtEq (r1,r2) -> [|r1;r2|] 
         | CondLtEq (r1,r2) -> [|r1;r2|] 
    
    type AssignmentEffect =  Simple of VarDef*VarDefRef
                             | CallAndAssign of VarDef*FuncCall
   
//    type Label = Label of SideEffect //Instruction (allow modification of labels)
//                 //| BlockLabel of SideEffect
    and
        [<ReferenceEquality>]
        SideEffect = Assign of AssignmentEffect  //to do: replace effect with stmt
                      | Call of FuncCall
                      | Goto of SideEffectRef //to do: change to goto of SideEffect
                      | CondGoto of SideEffectRef*Cond
                      | Target of (string ref)
                      | Return of option<VarDefRef>
                      //| Throw 
                      | Block of SideEffectRef []
                      | Nop
        and
            SideEffectRef = SideEffectRef of ref<SideEffect>

    let labelToStringBuilder (sb: System.Text.StringBuilder) (stmt: SideEffect) = 
        match stmt with
         | Target (name) -> sb.Append(sprintf "LABEL_%s" !name)
         | _ -> failwith "oops, incorrect target"

    let getDefFromRef(VarDefRef r) = !r

    let rec funcCallToString (sb: System.Text.StringBuilder) (call: FuncCall) = 
        let binOper (name: string) d1 d2 = 
            (varDefToStringBuilder sb d1).Append(" ").Append(name).Append(" ") |> ignore
            varDefToStringBuilder sb d2 |> ignore 
            
        match call with
         | FuncCall (mr,d) -> 
            let name = if mr.Name = ".ctor" then mr.DeclaringType.Name else mr.Name
            sb.Append(name).Append("(") |> ignore
            d |> Array.iter(fun i -> (varDefToStringBuilder sb (getDefFromRef i)).Append(", ")|> ignore )
            sb.Append(")") |> ignore
         | BuiltInCall1 (b,d) ->
                match b with 
                 | Box tr -> sb.Append("(").Append(tr.Name).Append(")") |> ignore
                             varDefToStringBuilder sb (getDefFromRef d) |> ignore
         | BuiltInCall2 (b, d1, d2) ->
            let d1 = getDefFromRef d1
            let d2 = getDefFromRef d2
            match b with 
             | Add -> binOper "+" (d1) (d2)
             | Sub -> binOper "-" (d1) (d2)
         | Flag (cmp,d1,d2) ->
            let d1 = getDefFromRef d1
            let d2 = getDefFromRef d2
            match cmp with
             | Eq -> binOper "==" d1 d2
             | Gt -> binOper ">" d1 d2
             | Lt -> binOper "<" d1 d2
    and
      varDefToStringBuilder (sb: System.Text.StringBuilder) ( def: VarDef): System.Text.StringBuilder = 
        match def with
         | Const v -> sb.Append(v)
         | LocalVar vd -> sb.Append(vd.Name)
         | MethodArg pd -> sb.Append(pd.Name)
         | Field fd -> sb.Append(fd.Name)
         | FunPtr mr -> sb.Append(mr.Name)
         | TmpVar (t,_) -> sb.Append(sprintf "tmp_%d" t.Offset)
         | ExprOfCall c -> funcCallToString sb c; sb



    let isDefTmpVar (def: VarDefRef) =
         
        match (getDefFromRef def) with
         | TmpVar (t,_) -> true
         | _ -> false


//    let labelToStringBuilder (sb: System.Text.StringBuilder) (Label (instr,name)) = 
//        sb.Append(sprintf "LABEL_%s" !name)




    ///type IfElseResult = 

//    type IfElseCond = 
//                  CondTrue                  //true
//                | CondFalse                 //false
//                | Eq of VarDef*VarDef       // = 
//                | Neq of VarDef*VarDef      // <>
//                | Lt of VarDef*VarDef       // <
//                | Gt of VarDef*VarDef       // >
//                | GtEq of VarDef*VarDef     //<=
//                | LtEq of VarDef*VarDef     //>=


    let condToString (sb: System.Text.StringBuilder) (cond: Cond) = 
        let binOper (name: string) d1 d2 = 
            (varDefToStringBuilder sb d1).Append(" ").Append(name).Append(" ") |> ignore
            varDefToStringBuilder sb d2 |> ignore 
            

        match cond with
         | CondTrue d -> varDefToStringBuilder sb (getDefFromRef d) |> ignore
         | CondFalse d -> sb.Append("not(") |> ignore
                          varDefToStringBuilder sb (getDefFromRef d) |> ignore
                          sb.Append(")") |> ignore
         | CondEq (d1,d2) ->    let d1, d2 = getDefFromRef d1, getDefFromRef d2 in binOper " == " d1 d2
         | CondNeq (d1, d2) ->  let d1, d2 = getDefFromRef d1, getDefFromRef d2 in binOper " != " d1 d2
         | CondLt (d1, d2) ->   let d1, d2 = getDefFromRef d1, getDefFromRef d2 in binOper " < " d1 d2
         | CondGt (d1, d2) -> let d1, d2 = getDefFromRef d1, getDefFromRef d2 in binOper " > " d1 d2
         | CondGtEq (d1, d2) -> let d1, d2 = getDefFromRef d1, getDefFromRef d2 in binOper " >= " d1 d2
         | CondLtEq (d1, d2) -> let d1, d2 = getDefFromRef d1, getDefFromRef d2 in binOper " <= " d1 d2

    let getLHCond (cond: Cond) = 
        match cond with 
         | CondTrue d ->    [|d|]
         | CondFalse d ->   [|d|]
         | CondEq (d1, d2) ->   [|d1;d2|]
         | CondNeq (d1, d2) ->  [|d1;d2|]
         | CondLt (d1, d2) ->   [|d1;d2|]
         | CondGt (d1, d2) ->   [|d1;d2|]
         | CondGtEq (d1, d2) -> [|d1;d2|]
         | CondLtEq (d1, d2) -> [|d1;d2|]

                             //| IfElse of IfElseCond*IfElseResult*IfElseResult
    
    let assignmentEffectToString (sb: System.Text.StringBuilder) (effect: AssignmentEffect) = 
        match effect with
         | Simple (d1,d2) -> (varDefToStringBuilder sb (d1)).Append(" = ") |> ignore
                             (varDefToStringBuilder sb (getDefFromRef d2)) |> ignore
         | CallAndAssign (d,c) -> 
                             (varDefToStringBuilder sb d).Append(" = ") |> ignore
                             funcCallToString sb c |> ignore

    let getLHFunCall(call: FuncCall) = 
        match call with
         | FuncCall (_, ds) -> ds
         | BuiltInCall1 (_,d) -> [|d|]
         | BuiltInCall2 (_,d1,d2) -> [|d1;d2|]
         | Flag (_,d1,d2) -> [|d1;d2|]

    let getLHSDef (effect: SideEffect) = 
        match effect with
         | Assign (Simple(d1,d2)) -> [|d2|]
         | Assign (CallAndAssign(_,c)) -> getLHFunCall c
         | Call c -> getLHFunCall c
         | Goto _ | Target _ -> [||]
         | CondGoto (_,cond) -> getLHCond cond
         | Return None -> [||]
         | Return (Some v) -> [|v|]
      
    let getRHSDef (SideEffectRef r) = 
        match (!r) with
         | SideEffect.Assign (AssignmentEffect.Simple(d,VarDefRef r)) -> Some (d,!r)
         | SideEffect.Assign (AssignmentEffect.CallAndAssign(d,c)) -> Some (d, VarDef.ExprOfCall c)
         | _ -> None
          

    let getEffectFromRef (SideEffectRef effect: SideEffectRef) = !effect
         
    let sideEffectToString (sb: System.Text.StringBuilder) effect  = 
        match getEffectFromRef effect with
         | Assign assign -> assignmentEffectToString sb assign
         | Call call -> funcCallToString sb call
         | Goto ( label) -> 
                         sb.Append("goto ") |> ignore
                         labelToStringBuilder sb (getEffectFromRef label) |> ignore
         | CondGoto (lab, cond) -> sb.Append("if (") |> ignore
                                   condToString sb cond |> ignore
                                   sb.Append(") goto ") |> ignore
                                   labelToStringBuilder sb (getEffectFromRef lab) |> ignore
         | Target lab -> labelToStringBuilder sb (getEffectFromRef effect) |> ignore
                         sb.Append(":") |> ignore
         | Return None ->
                sb.Append("return") |> ignore
         | Return (Some v) ->
                sb.Append("return") |> ignore
                varDefToStringBuilder sb (getDefFromRef v) |> ignore
         | Nop -> ()
    let pop n values = 
        Seq.take n values |> List.ofSeq,
         Seq.skip n values |> List.ofSeq
    
    let pop1 values = 
        List.head values, List.tail values

    let pop2 values = 
        let h::t::rest = values
        (h,t),rest

    let toDict(input:seq<'k*'v>) = 
        let d = System.Collections.Generic.Dictionary<_,_>()
        for (k,v) in input do
            d.Add(k,v)
        d

    let toGraph(instrs: Cil.Instruction[]): StronglyConnectedComponents.Graph<Cil.Instruction> = 
        let nodes = instrs |> Seq.map(fun instr -> instr, StronglyConnectedComponents.newNode(instr))
                    |> toDict

        let getNextInstrunctions(i: int) (instr: Cil.Instruction) = 
            let flowControl = instr.OpCode.FlowControl
            match flowControl with
             | Cil.FlowControl.Meta -> [|instrs.[i + 1]|]
             | Cil.FlowControl.Call -> [|instrs.[i + 1]|]
             | Cil.FlowControl.Next -> [|instrs.[i + 1]|] //to do: handle incorrect il
             | Cil.FlowControl.Branch -> [|instr.Operand :?> Cil.Instruction |]
             | Cil.FlowControl.Cond_Branch -> [|instrs.[i + 1]; instr.Operand :?> Cil.Instruction |]
             | Cil.FlowControl.Throw -> Array.empty
             | Cil.FlowControl.Return -> Array.empty
             | _ -> failwith (sprintf "Oops %A is unhandled" flowControl)
        instrs |> Seq.iteri(fun i instr -> let links = getNextInstrunctions i instr
                                           let node = nodes.[instr]
                                           for link in links do
                                              node.Links.Add(nodes.[link]))
        nodes.Values |> Array.ofSeq |> StronglyConnectedComponents.Graph
//          
//                        
    let graphByNode(StronglyConnectedComponents.Graph lst: StronglyConnectedComponents.Graph<'a>) =
        lst |> Seq.map(fun x -> x.Value,x.Links|>Seq.map(fun y -> y.Value)|>Array.ofSeq) |> toDict

    let toComponents(instrs: Cil.Instruction[]): (Cil.Instruction[][]*
                                                  System.Collections.Generic.Dictionary<Instruction, 
                                                                                        Instruction[]>)
                                                   =
        let graph = toGraph instrs
        let c, g = StronglyConnectedComponents.stronglyConnectedComponents graph
        c |> Array.map(fun c -> 
                        Array.map(fun (node: StronglyConnectedComponents.Node<Cil.Instruction>) -> node.Value) c
                       // |> Array.sortBy(fun instr -> instr.Offset)
                        ), graphByNode g 

//    let statementsToBlocks (stmts: SideEffect[]) = 
//        let mutable i = 0
//        let current = System.Collections.Generic.List<SideEffect>()
//        let blockList = System.Collections.Generic.List<SideEffect>()
//        let target = ref None
//        let finishCurrent () =
//            if (current.Count = 0) then
//                blockList.Add(SideEffect.Block((!target).Value, Array.ofSeq current))
//            target := None
//            current.Clear()
//        while (i < stmts.Length) do
//            let s = stmts.[i]
//            match s with
//             | Goto label -> current.Add(s); finishCurrent ()
//             | CondGoto (label,cond) -> current.Add(s); finishCurrent()
//             | Return v -> current.Add(s); finishCurrent()
//             | Target lbl -> finishCurrent();
//                             target := Some lbl
//                             current.Add(s)//??
//             | _ -> current.Add(s)
//            i <- i + 1
//        finishCurrent()
//        
//        let getLabel (maybeStmt: option<SideEffect>) = 
//            match maybeStmt with
//             | None -> failwith "no stmt"
//             | Some v ->
//                match v with
//                 | SideEffect.Block(lbl,_) -> lbl
//
//
//        let getTargets (stmt: SideEffect) (nextStmt: option<SideEffect>) = 
//            match stmt with
//             | SideEffect.Block (_,lst) -> 
//                let last = lst.[lst.Length - 1]
//                match last with
//                 | Goto lbl -> [|lbl|]
//                 | CondGoto (lbl,cond) -> [|lbl; getLabel nextStmt|]
//                 | _ -> [||]
//                                           
//
//        let toGraph(blocks: SideEffect []): StronglyConnectedComponents.Graph<SideEffect> = 
//            let nodes = blocks |> Seq.map(fun block -> block, StronglyConnectedComponents.newNode(block))
//                        |> toDict
//            for i in 0..(blocks.Length - 1) do
//                let b = blocks.[i]
//                let n = if i = blocks.Length - 1 then None else Some blocks.[i + 1]
//                let links = getTargets b n
//                let node = nodes.[b]
//                for link in links do
//                    
//                    node.Links.Add(nodes.[link])
//            nodes.Values |> Array.ofSeq |> StronglyConnectedComponents.Graph

//        ()

//    let stmtsToGraph(instrs: SideEffect[]): StronglyConnectedComponents.Graph<SideEffect> = 
//        let nodes = instrs |> Seq.map(fun instr -> instr, StronglyConnectedComponents.newNode(instr))
//                    |> toDict
//        let getNext (i: int) = instrs.[i + 1]
//        let getNextEffects(i: int) (effect: SideEffect) = 
//            match effect with
//             | SideEffect.Goto pos -> [|pos|]
//             | SideEffect.CondGoto (pos,_) -> [|pos;getNext i|]
//             | SideEffect.Return _ -> [||]
//             | _ -> [|getNext i|]
//        let graph = nodes.Values |> Array.ofSeq |> StronglyConnectedComponents.Graph
//        graph
    
    let mkDefRef v = VarDefRef(ref v)
    let mkSideEffectRef v = SideEffectRef(ref v)

    let decompile(methodDef: MethodDefinition) =
        let walkInstructions(instrs: Instruction[]) = 
            let labels = System.Collections.Generic.Dictionary<Instruction, SideEffect>()
            let getLabel(instr: Instruction) = 
                SideEffect.Target(ref (sprintf "Lab_%d" instr.Offset))

            instrs |> Seq.choose(fun instr -> match instr.Operand with
                                               | :? Instruction as target -> Some target
                                               | _ -> None)
            |> Seq.distinct
            |> Seq.iter(fun target -> labels.Add(target, getLabel target))
            
            let mkTempVar(instr: Instruction) = 
                TmpVar(instr, ref None)

            let handleInstruction (instr: Instruction) (inputStack: VarDef list, sideEffects: SideEffect list) =
                printfn "Instr %A" instr

                let ldc(v: string) = (Const v)::inputStack, sideEffects      
                
                let ldc_i4(i: int) = ldc (i.ToString())
                
                let stloc (x: int) = 
                    let variable = methodDef.Body.Variables.[x]
                    let hd,tl = pop1 inputStack
                    tl, Assign(Simple(LocalVar variable, mkDefRef hd))::sideEffects
  
                let ldloc (x: int) = 
                    let variable = methodDef.Body.Variables.[x]
                    let value = LocalVar(variable)
                    value::inputStack, sideEffects

                let ldarg(index: int) = 
                    let parameter = methodDef.Parameters.[index]
                    let value = MethodArg parameter
                    value::inputStack, sideEffects

                let builtInCall2 (t: BuiltInType2) = 
                    let (f,s),rest = pop2 inputStack
                    let tmp = mkTempVar instr
                    let methodCallEffect = Assign(CallAndAssign(tmp, BuiltInCall2(t, mkDefRef f, mkDefRef s)))
                    tmp::rest, methodCallEffect::sideEffects
           
                let builtInCall1 (t: BuiltInType1) = 
                    //let t = operandAsTypeReference() 
                    let arg,rest = pop1 inputStack
                    let temp = mkTempVar instr
                    let effect = Assign(CallAndAssign(temp,BuiltInCall1(t,mkDefRef arg)))
                    temp::rest, effect::sideEffects
                  
                let flagCmp (t: FlagCmp) = 
                    let (f,s),rest = pop2 inputStack
                    let tmp = mkTempVar instr
                    let methodCallEffect = Assign(CallAndAssign(tmp, Flag(t,mkDefRef f, mkDefRef s)))
                    tmp::rest, methodCallEffect::sideEffects
                                           
                let getLabel () = 
                    let instr = instr.Operand :?> Instruction
                    labels.[instr]

                let brCond1 (how) =
                    let arg,rest = pop1 inputStack
                    let label = getLabel ()
                    rest, SideEffect.CondGoto (mkSideEffectRef label, how (mkDefRef arg)) :: sideEffects
                           
                let brCond2 (how) =
                    let (arg1,arg2),rest = pop2 inputStack
                    let label = getLabel ()
                    rest, SideEffect.CondGoto (mkSideEffectRef label, how ((mkDefRef arg1),(mkDefRef arg2))) :: sideEffects
                                           
                let operandAsInt () = instr.Operand :?> int
                let operandAsString () = instr.Operand :?> string
                let operandAsByte () = instr.Operand :?> sbyte
                let operandAsTypeReference () = instr.Operand :?> TypeReference
                let operandAsFieldDefinition () = instr.Operand :?> FieldDefinition
                let operandAsMethodReference () = instr.Operand :?> MethodReference

                let funcCall (isCtor: bool) = 
                    let methodRef = (instr.Operand :?> MethodReference)
                    let extra = if methodRef.HasThis && not isCtor then 1 else 0 
                    let cnt = methodRef.Parameters.Count
                    let args, rest = pop (extra + cnt) inputStack
                    let sideEffectsNew = ""
                    let sb = System.Text.StringBuilder()
                    
                    let args = args |> List.rev |> Seq.map mkDefRef |> Array.ofSeq // |> Seq.map(fun (VarDef a) -> a) |> String.concat ","
                    let methodCall = FuncCall(methodRef, args)
                    
                    if (not isCtor && methodRef.ReturnType.FullName = "System.Void") then //to do:
                        rest,SideEffect.Call(methodCall)::sideEffects
                    else
                       let t = mkTempVar instr
                       let methodCallEffect = Assign(CallAndAssign(t, methodCall))
                       t::rest, methodCallEffect::sideEffects

                match instr.OpCode.Code with
                 | Cil.Code.Nop ->  inputStack, sideEffects
                 | Cil.Code.Ldarg_0 -> ldarg 0
                 | Cil.Code.Ldarg_1 -> ldarg 1
                 | Cil.Code.Ldarg_2 -> ldarg 2
                 | Cil.Code.Ldarg_3 -> ldarg 3
                 | Cil.Code.Ldarg_S -> ldarg (operandAsInt ())
                 | Cil.Code.Pop -> List.tail inputStack, sideEffects
                 | Cil.Code.Ldstr ->
                    let s = operandAsString ()
                    (Const s)::inputStack, sideEffects
                 | Cil.Code.Ldnull -> ldc "null"
                 | Cil.Code.Ldc_I4_0 -> ldc_i4 0
                 | Cil.Code.Ldc_I4_1 -> ldc_i4 1
                 | Cil.Code.Ldc_I4_2 -> ldc_i4 2
                 | Cil.Code.Ldc_I4_3 -> ldc_i4 3
                 | Cil.Code.Ldc_I4_4 -> ldc_i4 4
                 | Cil.Code.Ldc_I4_5 -> ldc_i4 5
                 | Cil.Code.Ldc_I4_6 -> ldc_i4 6
                 | Cil.Code.Ldc_I4_7 -> ldc_i4 7
                 | Cil.Code.Ldc_I4_8 -> ldc_i4 8
                 | Cil.Code.Ldc_I4_S -> ldc_i4 (int(operandAsByte()))
                 | Cil.Code.Ldc_I4 -> ldc_i4 (operandAsInt())
                 | Cil.Code.Ldftn ->
                    let value = VarDef.FunPtr (operandAsMethodReference()) 
                    value::inputStack, sideEffects                    
                 | Cil.Code.Stloc_0 -> stloc 0
                 | Cil.Code.Stloc_1 -> stloc 1
                 | Cil.Code.Stloc_2 -> stloc 2  
                 | Cil.Code.Stloc_3 -> stloc 3  
                 | Cil.Code.Stloc_S -> stloc (operandAsInt ())
                 | Cil.Code.Ldloc_0 -> ldloc 0
                 | Cil.Code.Ldloc_1 -> ldloc 1
                 | Cil.Code.Ldloc_2 -> ldloc 2  
                 | Cil.Code.Ldloc_3 -> ldloc 3  
                 | Cil.Code.Ldloc_S -> ldloc (operandAsInt()) 
                 | Cil.Code.Call -> funcCall false
                 | Cil.Code.Add -> builtInCall2 (BuiltInType2.Add)
                 | Cil.Code.Sub -> builtInCall2 (BuiltInType2.Sub)
                 | Cil.Code.Box -> builtInCall1 (BuiltInType1.Box(operandAsTypeReference()))

                 | Cil.Code.Ceq -> flagCmp FlagCmp.Eq
                 | Cil.Code.Cgt -> flagCmp FlagCmp.Gt
                 | Cil.Code.Clt -> flagCmp FlagCmp.Lt

                 | Cil.Code.Br | Cil.Code.Br_S ->
                    let label = getLabel ()
                    inputStack, SideEffect.Goto (mkSideEffectRef label) :: sideEffects //to do: ops, have to verify the stack

                 | Cil.Code.Brtrue | Cil.Code.Brtrue_S -> brCond1 (Cond.CondTrue)
                 | Cil.Code.Brfalse | Cil.Code.Brfalse_S -> brCond1 (Cond.CondFalse)
                 | Cil.Code.Beq | Cil.Code.Beq_S -> brCond2 (Cond.CondEq)
                 | Cil.Code.Bge | Cil.Code.Bge_S -> brCond2 Cond.CondGtEq
                 | Cil.Code.Ble | Cil.Code.Ble_S -> brCond2 Cond.CondLtEq
                 | Cil.Code.Bgt | Cil.Code.Bgt_S -> brCond2 Cond.CondGt
                 | Cil.Code.Blt | Cil.Code.Blt_S -> brCond2 Cond.CondLt
                 | Cil.Code.Newobj -> funcCall true
                 | Cil.Code.Ldsfld -> //of this object
                    let def = operandAsFieldDefinition ()
                    VarDef.Field def::inputStack, sideEffects
                 | Cil.Code.Stsfld ->
                    let hd,tl = pop1 inputStack
                    let def = operandAsFieldDefinition ()
                    tl, Assign(Simple(VarDef.Field def, mkDefRef hd))::sideEffects
                    
                 //| Cil.Code.Brtrue | Cil.Code.Brtrue_S ->
//                    let (VarDef arg1):: rest = inputStack
//                    let methodCall = sprintf "if (%s) goto ???" arg1
//                    [], SideEffect methodCall::sideEffects
//                 | Cil.Code.Br | Cil.Code.Br_S ->
//                    
//                    let methodCall = "goto ???"
//                    [], SideEffect methodCall::sideEffects
                 | Cil.Code.Ret ->
                    if (methodDef.ReturnType.FullName = "System.Void") then
                        [], SideEffect.Return None ::sideEffects
                    else
                        let arg1,_ = pop1 inputStack
                        [], SideEffect.Return(Some (mkDefRef arg1))::sideEffects
                 | _ -> failwith (sprintf "not implemented %A operand (%s)" instr (instr.Operand.GetType().ToString()) )



            //let isTarget(instr: Instruction) = false
            let stacksAndEffects = new System.Collections.Generic.Dictionary<Instruction, VarDef list>()
            let components, revLinks = toComponents instrs
            let getParentStack(i: Instruction) = 
                let l = revLinks.[i]
                if l.Length = 0 then []
                else 
                    l |> Seq.choose(fun x -> let ok,v = stacksAndEffects.TryGetValue(x)
                                             if ok then Some v else None) |> Seq.head
                    
            let sideEffects = new System.Collections.Generic.Dictionary<Instruction, SideEffectRef list>()
            let printStack (l: VarDef list) = 
                printfn "Stack--------------------"
                List.iter(fun x -> printfn "%A" x) l
                printfn "--------------------"
            for comp in components do
                printfn "Begin Component"
                comp |> Seq.iter (fun i -> printf "*%s," (i.ToString()))
                for instr in comp do
                    printfn "\n\n\n   Instr %s" (instr.ToString())
                    let s = getParentStack instr
                    
                    //printStack s
                    let maybeTarget = if labels.ContainsKey(instr) then [labels.[instr]] else []
                    let n,effects = handleInstruction instr (s, maybeTarget)
                    sideEffects.[instr] <- List.rev effects |> List.map mkSideEffectRef
                    //printfn "After"
                    //printStack n
                    stacksAndEffects.Add(instr, n)
                    //System.Console.ReadKey() |> ignore
            let allEffects = Seq.map2 (fun a b -> (a,b)) sideEffects.Keys sideEffects.Values
                             |> Seq.sortBy(fun (a,b) -> a.Offset) |> Seq.map snd
                             |> Seq.concat |> Array.ofSeq
            printfn "\n\n\n*****************************"
            let effectToStr e = 
                let sb = System.Text.StringBuilder()
                sideEffectToString sb e
                sb.ToString()

            let simplifyEffects(effects: SideEffectRef[]) =
               // let graph = stmtsToGraph effects 
                //let components, _ = StronglyConnectedComponents.stronglyConnectedComponents graph

                let lhsVars = 
                    effects |> Seq.map (fun (SideEffectRef r) -> !r) |> Seq.collect getLHSDef |> Seq.countBy id
                    |> Seq.filter(fun (d,i) -> isDefTmpVar d && i = 1) 
                    |> Array.ofSeq
                 
                lhsVars 
                let defUsedOnlyOnce =   
                    lhsVars |> Seq.map(fun (VarDefRef d,i) -> (!d,i)) |> toDict 

                //
                //for e in effects do
                    
                //find all assignments usedOnlyOnce = ... and remove them
                //find all l.h.s sites where used only once is used
                let rhsVars =
                    effects |> Seq.choose (fun r ->
                                                match getRHSDef r with
                                                 | Some v -> Some(r,v)
                                                 | None -> None)
                    |> Seq.filter (fun (SideEffectRef r,(v,_)) -> defUsedOnlyOnce.ContainsKey(v) )
                    |> Array.ofSeq

                let d = rhsVars |>  Seq.map (fun (SideEffectRef r,v) -> v) |> toDict
                lhsVars |> Seq.iter (fun (VarDefRef r,_) -> r := d.[!r])
                //replace with nop
                rhsVars |>  Seq.iter (fun (SideEffectRef r,(v,_)) -> 
                                                r := SideEffect.Nop)

                effects
            
            simplifyEffects allEffects
            allEffects |> Array.map (fun r -> effectToStr r) |> Array.iter (printfn "%s")

//            let mutable i = 0
//            let mutable current = [],[]

//            let results = System.Collections.Generic.List<VarDef list*SideEffect list>()
//            let sideEffects = System.Collections.Generic.List<SideEffect list>()
//            while i < instrs.Length do
//                let instr = instrs.[i]
//                if (isTarget instr) then
//                    current <- [],snd current
//                current <- handleInstruction instr current
//                results.Add(current)
//                i <- i + 1
//            
//            results |> Array.ofSeq
        let r = walkInstructions (methodDef.Body.Instructions |> Array.ofSeq)
        r |> ignore
//        printfn "---------------"
//        r.[r.Length - 1]|>snd |> List.rev |> Seq.iter(fun ( e) -> printfn "%s" (e.ToString()))
//        printfn "---------------"
            
                

module Start = 
    open Mono.Cecil


    let testGraph () = 
        let makeNode (x: string) = 
            StronglyConnectedComponents.newNode x

        let a = makeNode "a"
        let b = makeNode "b"
        let c = makeNode "c"
        let d = makeNode "d"
        let e = makeNode "e"
        let f = makeNode "f"
        let g = makeNode "g"
        let h = makeNode "h"

        let graph = [|c;g;f;h;d;b;a;e|] //[|a;b;c;d;e;f;g;h|]
        let link(x: StronglyConnectedComponents.Node<_>, y: StronglyConnectedComponents.Node<_>) = 
            x.Links.Add(y)

        link(c, g)

        link(a,b)
        link(b,c)
        link(c,d)
        link(d,c)

        link(e, a)
        link(b, e)
        link(b, f)
        
        link(d, h)

        link(e, f)
        link(f, g)
        link(g, f)
        link(g, h)
        link(h, h)

        let components = StronglyConnectedComponents.stronglyConnectedComponents(StronglyConnectedComponents.Graph graph)|> fst
        components 
        |> Seq.iter(fun c ->
                        printfn "---------------------------"
                        c |> Seq.iter(fun n -> printf "%s, " n.Value)
                        )

//     L_0000: nop            [],[]
//    L_0001: ldc.i4.0       [],[0]
//    L_0002: stloc.0        [0], [], side effecs = {x = 0}
//    L_0003: ldc.i4.1       [], [1], ...
//    L_0004: stloc.1        [1],[], side effects = {x= 0, y = 1}
//    L_0005: ldloc.0        [],[x]
//    L_0006: ldloc.1        [],[x;y]
//    L_0007: add            [x;y],[tmp7], side effects = {x = 0;y=1, tmp7 = add(x,y)}
//    L_0008: stloc.2        [tmp7],[], side effects = {..., z = tmp 7}
//    L_0009: ldloc.0        [],[x]
//    L_000a: ldloc.1        [],[x,y]
//    L_000b: ceq            [x,y], [tmp_0b], se. {..., tmp_0 = (x = y) ? 1: 0
//    L_000d: ldc.i4.0       [tmp_0b, 0]
//    L_000e: ceq                    [tmp_0e]
//    L_0010: stloc.3        ...
//    L_0011: ldloc.3        ...
//    L_0012: brtrue.s L_0017 if z = 1 then goto 017
//    L_0014: nop        
//    L_0015: br.s L_0009     goto ...
//    L_0017: ret 	    return
//}
//


    [<EntryPoint>]
    [<System.STAThread()>]           
    let main args =
        //AssemblyInfo2UI.populateForm()
        let assemblyName = @" D:\DotNetTracer\DotNetTracer\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe"
        //let assemblyName = @" D:\DotNetTracer\DotNetTracer\SimpleAssemblyBrowserInFSharp\bin\Debug\SimpleAssemblyBrowserInFSharp.exe"
        let assemblyDef = AssemblyDefinition.ReadAssembly(assemblyName)
        assemblyDef.MainModule.ReadSymbols()
        let methodDef = assemblyDef.EntryPoint
        CodeUtils.dumpCode methodDef
        printfn "Basic Blocks"
//        CodeUtils.basicBlocks (methodDef.Body.Instructions |> Array.ofSeq)
//        |> Seq.iter(fun c ->
//                        printfn "---------------------------"
//                        c |> Seq.iter(fun n -> printf "%s, " (n.ToString()))
//                        printfn ""
//                        )
//        testGraph ()
        CodeDecompil.decompile methodDef
        0

//    [<EntryPoint>]
//    [<System.STAThread()>]           
//    let main args =    
//        CodeUtils.Unif.test ()
//
//        0

//instr -> graph in order to infer the order of processing
//instr -> infer call stack contents -> var, statments
//statements -> graph -> blocks 
//process blocks, e.g. find entry and exit and merge
//replace: goto L15; -> remove this
//         L15; -> 
//remove labels: if no one points to them
//handle special cases, then redo all from variable substibution onwards

//sum = 0
//i = 0
//goto L15  
//L7: 
//sum = i + sum
//i = i + 1
//L15:  -> entry -> exit
//if (10 < i) goto 7
//
//WriteLine(...,(object)sum)
//
//
//get component,
//let entry = findEntry()
//let exit = findExit()
//if (entry = exit) then
//
//goto block: //if its just one block, then replace goto with block
// while(cond){
// sum <- i + sum
// i <- i + 1
//}